#include "VxIrrLib.h"

//============================================================================
//! Convert irrlicht mesh to Bullet Triangle Array
btTriangleIndexVertexArray * IrrPhysMeshToBulletTriangleArray(	irr::scene::IMesh* poMesh,
																const irr::core::vector3df& v3fScaleIn )
{
	btVector3 vertices[3];
	irr::u32 j;
	irr::u32 numVertices;
	irr::u32 numIndices;
	irr::u16* aIrrIndices;

	const int vertStride = sizeof(btVector3);
	const int indexStride = 3 * sizeof(int);
	int totalTriangles;

	btTriangleIndexVertexArray* indexVertexArrays = new btTriangleIndexVertexArray();
	btIndexedMesh aIndexedMesh;

	for( irr::u32 i = 0; i < poMesh->getMeshBufferCount(); i++ )
	{
		irr::scene::IMeshBuffer* mb = poMesh->getMeshBuffer(i);

		if( irr::video::EVT_STANDARD == mb->getVertexType() )
		{
			irr::video::S3DVertex* aIrrVertices=(irr::video::S3DVertex*)mb->getVertices();
			aIrrIndices = mb->getIndices();
			numVertices = mb->getVertexCount();
			numIndices = mb->getIndexCount();
			totalTriangles = numIndices/3;


			// TODO: don't forget delete []
			int* aBtIndexBuffer = new int[numIndices];
			btVector3* aBtVertBuffer = new btVector3[numVertices];

			// fill ind
			for (j = 0; j < numIndices; j++)
			{
				aBtIndexBuffer[j] = aIrrIndices[j];
			}
			// fill vert
			for (j = 0; j < numVertices; j++)
			{
				aBtVertBuffer[j].setValue(aIrrVertices[j].Pos.X, aIrrVertices[j].Pos.Y, aIrrVertices[j].Pos.Z);
			}

			aIndexedMesh.m_numTriangles			= totalTriangles;
			aIndexedMesh.m_triangleIndexBase	= (const unsigned char *)aBtIndexBuffer;
			aIndexedMesh.m_triangleIndexStride	= indexStride;
			aIndexedMesh.m_numVertices			= numVertices;
			aIndexedMesh.m_vertexBase			= (const unsigned char *) &aBtVertBuffer[0].x();
			aIndexedMesh.m_vertexStride			= vertStride;
			indexVertexArrays->addIndexedMesh(aIndexedMesh);
		}
		else if( irr::video::EVT_2TCOORDS == mb->getVertexType() )
		{
			irr::video::S3DVertex2TCoords* aIrrVertices=(irr::video::S3DVertex2TCoords*)mb->getVertices();
			aIrrIndices = mb->getIndices();
			numVertices = mb->getVertexCount();
			numIndices = mb->getIndexCount();
			totalTriangles = numIndices/3;

			int* aBtIndexBuffer = new int[numIndices];
			btVector3* aBtVertBuffer = new btVector3[numVertices];

			// fill ind
			for (j = 0; j < numIndices; j++)
			{
				aBtIndexBuffer[j] = aIrrIndices[j];
			}
			// fill vert
			for (j = 0; j < numVertices; j++)
			{
				aBtVertBuffer[j].setValue(	aIrrVertices[j].Pos.X * v3fScaleIn.X,
											aIrrVertices[j].Pos.Y * v3fScaleIn.Y,
											aIrrVertices[j].Pos.Z * v3fScaleIn.Z );
			}

			aIndexedMesh.m_numTriangles = totalTriangles;
			aIndexedMesh.m_triangleIndexBase = (const unsigned char *)aBtIndexBuffer;
			aIndexedMesh.m_triangleIndexStride = indexStride;
			aIndexedMesh.m_numVertices = numVertices;
			aIndexedMesh.m_vertexBase = (const unsigned char *)&aBtVertBuffer[0].x();
			aIndexedMesh.m_vertexStride = vertStride;
			indexVertexArrays->addIndexedMesh(aIndexedMesh);
		}
		else if( irr::video::EVT_TANGENTS == mb->getVertexType() )
		{
			vx_assert( false );
		}
		else
		{
			vx_assert( false );
		}
	}

	return indexVertexArrays;
}

//============================================================================
//! Convert irrlicht mesh to Bullet Triangle Mesh
btTriangleMesh* IrrPhysMeshToBulletTriangleMesh(	irr::scene::IMesh* poMesh,
													const irr::core::vector3df& v3fScaleIn )
{
	btVector3 vertices[3];
	irr::u32 j;
	irr::u32 k;
	irr::u32 index;
	irr::u32 numVertices;
	irr::u32 numIndices;
	irr::u16* mb_indices;

	btTriangleMesh *pTriMesh = new btTriangleMesh();

	for( irr::u32 i = 0; i < poMesh->getMeshBufferCount(); i++ )
	{
		irr::scene::IMeshBuffer* mb=poMesh->getMeshBuffer(i);
		if( irr::video::EVT_STANDARD == mb->getVertexType() )
		{
			irr::video::S3DVertex* mb_vertices=(irr::video::S3DVertex*)mb->getVertices();
			mb_indices = mb->getIndices();
			numVertices = mb->getVertexCount();
			numIndices = mb->getIndexCount();
			for(j=0;j<numIndices;j+=3)
			{ 
				//get index into vertex list
				for (k=0;k<3;k++)
				{ 
					//three verts per triangle
					index = mb_indices[j+k];
					//convert to btVector3
					// we apply scaling factor directly to verticies
					vertices[k] = btVector3(	mb_vertices[index].Pos.X * v3fScaleIn.X,
												mb_vertices[index].Pos.Y * v3fScaleIn.Y,
												mb_vertices[index].Pos.Z * v3fScaleIn.Z);
				}
				pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
			}


			//FILE* aMeshFile = fopen("mesh.dat", "w");
			//if (aMeshFile)
			//{
			//  fprintf(aMeshFile, "const int NUM_TRIANGLES = %d;\n", mb->getIndexCount()/3);
			//  fprintf(aMeshFile, "const int NUM_INDICES = %d;\n", mb->getIndexCount());
			//  fprintf(aMeshFile, "const int NUM_VERTICES = %d;\n", numVertices);

			//  fprintf(aMeshFile, "REAL gVertices[NUM_VERTICES * 3] = {\n");
			//  for (k = 0; k < numVertices; k++)
			//  {
			//    fprintf(aMeshFile, "  REAL(%10.7f), REAL(%10.7f), REAL(%10.7f),\n",
			//      mb_vertices[k].Pos.X, mb_vertices[k].Pos.Y, mb_vertices[k].Pos.Z
			//    );
			//  }
			//  fprintf(aMeshFile, "};\n");

			//  fprintf(aMeshFile, "int gIndices[NUM_TRIANGLES][3] = {\n");
			//  for (k = 0; k < mb->getIndexCount(); k+=3)
			//  {
			//    fprintf(aMeshFile, "  {%4d, %4d, %4d},\n",
			//      mb_indices[k+0], mb_indices[k+1], mb_indices[k+2]
			//    );
			//  }
			//  fprintf(aMeshFile, "};\n");

			//  fclose(aMeshFile);
			//}

		}
		else if( irr::video::EVT_2TCOORDS == mb->getVertexType() )
		{
			irr::video::S3DVertex2TCoords* mb_vertices=(irr::video::S3DVertex2TCoords*)mb->getVertices();
			mb_indices = mb->getIndices();
			numVertices = mb->getVertexCount();
			numIndices = mb->getIndexCount();
			for(j=0;j<numIndices;j+=3)
			{ //index into irrlicht data
				for (k=0;k<3;k++)
				{
					index = mb_indices[j+k];
					// we apply scaling factor directly to verticies
					vertices[k] = btVector3(	mb_vertices[index].Pos.X * v3fScaleIn.X,
												mb_vertices[index].Pos.Y * v3fScaleIn.Y,
												mb_vertices[index].Pos.Z * v3fScaleIn.Z);
				}
				pTriMesh->addTriangle( vertices[0], vertices[1], vertices[2] );
			}
		}
		else if( irr::video::EVT_TANGENTS == mb->getVertexType() )
		{
			vx_assert( false );
		}
		else
		{
			vx_assert( false );
		}
	}

	return pTriMesh;
}

//============================================================================
//! Convert irrlicht mesh to Bullet Convex Hull Shape
//! ConvexHullShape implements an implicit (getSupportingVertex) Convex Hull of a Point Cloud (vertices)
//! No connectivity is needed.
btConvexHullShape* IrrPhysMeshToBulletConvexHullShape(	irr::scene::IMesh*			poMesh,
														const irr::core::vector3df&	v3fScaleIn )
{
	btVector3 vertices[3];
	irr::u32 i,j,k,index,numVertices;
	irr::u16* mb_indices;

	irr::core::array<btVector3> aBtVertBuffer(512);

	for (i=0; i<poMesh->getMeshBufferCount(); i++)
	{
		irr::scene::IMeshBuffer* mb=poMesh->getMeshBuffer(i);

		//extract vertex data
		//because the vertices are stored as structs with no common base class,
		// we need to handle each type separately
		if(mb->getVertexType()==irr::video::EVT_STANDARD)
		{
			irr::video::S3DVertex* mb_vertices=(irr::video::S3DVertex*)mb->getVertices();
			mb_indices = mb->getIndices();
			numVertices = mb->getVertexCount();
			for(j=0;j<mb->getIndexCount();j+=3)
			{ //get index into vertex list
				for (k=0;k<3;k++)
				{ //three verts per triangle
					index = mb_indices[j+k];
					//convert to btPoint3
					aBtVertBuffer.push_back(btVector3(
						mb_vertices[index].Pos.X * v3fScaleIn.X,
						mb_vertices[index].Pos.Y * v3fScaleIn.Y,
						mb_vertices[index].Pos.Z * v3fScaleIn.Z));
				}
			}
		}
		else if(mb->getVertexType()==irr::video::EVT_2TCOORDS)
		{
			//same but for S3DVertex2TCoords data
			irr::video::S3DVertex2TCoords* mb_vertices=(irr::video::S3DVertex2TCoords*)mb->getVertices();
			irr::u16* mb_indices = mb->getIndices();
			irr::s32 numVertices = mb->getVertexCount();
			for(j=0;j<mb->getIndexCount();j+=3)
			{ //index into irrlicht data
				for (k=0;k<3;k++)
				{
					irr::s32 index = mb_indices[j+k];
					//convert to btPoint3
					aBtVertBuffer.push_back(btVector3(
						mb_vertices[index].Pos.X * v3fScaleIn.X,
						mb_vertices[index].Pos.Y * v3fScaleIn.Y,
						mb_vertices[index].Pos.Z * v3fScaleIn.Z));
				}
			}
		}
		//not bothering with EVT_TANGENTS vertex type
	}

	btScalar ratio = aBtVertBuffer[0].length();
	for (i = 0; i < aBtVertBuffer.size(); i++)
	{
		aBtVertBuffer[i].normalize();
	}
	btConvexHullShape* aConvexHullShape = new btConvexHullShape((btScalar*)&aBtVertBuffer[0], aBtVertBuffer.size());
	aConvexHullShape->setLocalScaling(btVector3(ratio, ratio, ratio));
	aConvexHullShape->recalcLocalAabb();
	return aConvexHullShape;
}


//! convert irrlicht terrain to bullet triangle mesh
btTriangleMesh * IrrPhysTerrainToBulletTriangleMesh(	irr::scene::ITerrainSceneNode * poTerrain,
														const irr::core::vector3df&		v3fScaleIn )
{
	//mesh converting (32index fix thanks to enif)

	btVector3 vertices[3];
	irr::u32 j;
	irr::u32 k;
	irr::u32 index;
	irr::u32 numVertices;
	irr::u32 numIndices;
	irr::u16* mb_indices16;
	irr::u32* mb_indices32;
	irr::video::E_INDEX_TYPE indicesType;
	btTriangleMesh *triangleMesh;

	irr::scene::CDynamicMeshBuffer* buffer = 0;
	numVertices = poTerrain->getMesh()->getMeshBuffer(0)->getVertexCount();
	if (numVertices <= 65536)
	{
		//small enough for 16bit buffers
		buffer = new irr::scene::CDynamicMeshBuffer( irr::video::EVT_2TCOORDS, irr::video::EIT_16BIT );
	}
	else
	{
		//we need 32bit buffers
		buffer = new irr::scene::CDynamicMeshBuffer( irr::video::EVT_2TCOORDS, irr::video::EIT_32BIT );
	}

	poTerrain->getMeshBufferForLOD(*buffer, 0);

	numVertices = buffer->getVertexCount();
	numIndices = buffer->getIndexCount();
	indicesType = buffer->getIndexType();

	if (indicesType == irr::video::EIT_16BIT)
	{
		mb_indices16 = buffer->getIndices();

		irr::video::S3DVertex2TCoords* mb_vertices = (irr::video::S3DVertex2TCoords*)buffer->getVertices();
		btTriangleMesh *pTriMesh = new btTriangleMesh();
		irr::core::vector3df v3fScale = v3fScale;

		for(j = 0; j < numIndices; j += 3)
		{
			//index into irrlicht data
			for (k = 0; k < 3; k++)
			{
				index = mb_indices16[j+k];

				// we apply scaling factor directly to verticies
				vertices[k] = btVector3(	mb_vertices[index].Pos.X * v3fScaleIn.X,
											mb_vertices[index].Pos.Y * v3fScaleIn.Y,
											mb_vertices[index].Pos.Z * v3fScaleIn.Z);
			}

			pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
		}

		triangleMesh = pTriMesh;
	}
	else if (indicesType == irr::video::EIT_32BIT)
	{
		mb_indices32 = (irr::u32*)buffer->getIndices();

		irr::video::S3DVertex2TCoords* mb_vertices = (irr::video::S3DVertex2TCoords*)buffer->getVertices();
		btTriangleMesh *pTriMesh = new btTriangleMesh();
		irr::core::vector3df v3fScale = v3fScaleIn;

		for(j = 0; j < numIndices; j += 3)
		{
			//index into irrlicht data
			for (k = 0; k < 3; k++)
			{
				index = mb_indices32[j+k];

				// we apply scaling factor directly to verticies
				vertices[k] = btVector3(	mb_vertices[index].Pos.X * v3fScaleIn.X,
											mb_vertices[index].Pos.Y * v3fScaleIn.Y,
											mb_vertices[index].Pos.Z * v3fScaleIn.Z);
			}
			pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
		}
		triangleMesh = pTriMesh;
	}
	else
	{
		// bad indices type!
		buffer->drop();
		vx_assert( false );
		return NULL;
	}

	buffer->drop();

	return  triangleMesh;

}


